#include "Dream.h"
#include "Satory.h"
#include "ChatWindow.h"

#include <QFile>
#include <QDir>

Dream::Dream(Satory* master,QString name):
    Master(master),mName(name),isAlive(true),isAuth(false),isInitSender(false),isInitReceiver(false),
    mItem(0),mChat(0),mMessageCounter(0)
{
    RSAKey& key=Master->GetMyKey();
    mReceiver.AcceptRSAKeys(key.cipher,key.sign,true);
}

void Dream::AttachUi(UiType type, void* attach)
{
    if(type==PERSON)
        mItem=(QListWidgetItem*)attach;
    if(type==CHAT)
        mChat=(ChatWindow*)attach;

}
void Dream::SetAuthStatus(bool val)
{
    isAuth=val;
    if(mItem)
         mItem->setTextColor(QColor(255,0,0));
}

bool Dream::AskAuth()
{
    if(mState==ASK_AUTH || mState==CONFIRM_AUTH)
        return false;

    DataBlock enc,sign;

    if(Satory::GetInstance()->PublicKeyGet(mName))
    {
        PublicKeyGetRequest* req=dynamic_cast<PublicKeyGetRequest*>(Satory::GetInstance()->GetTopRequest());
        if(req)
        {
            mState=ASK_AUTH;
            connect(req,SIGNAL(KeyArrived(BaseRequest*,Json::Value&)),this,SLOT(OnRequestArrived(BaseRequest*,Json::Value&)));
            return true;
        }

    }


    return false;
}


bool Dream::ConfirmAuth()
{
    if(mState==CONFIRM_AUTH || mState==ASK_AUTH)
        return false;

    DataBlock enc,sign;

    if(Satory::GetInstance()->PublicKeyGet(mName))
    {
        PublicKeyGetRequest* req=dynamic_cast<PublicKeyGetRequest*>(Satory::GetInstance()->GetTopRequest());
        if(req)
        {
            mState=CONFIRM_AUTH;
            connect(req,SIGNAL(KeyArrived(BaseRequest*,Json::Value&)),this,SLOT(OnRequestArrived(BaseRequest*,Json::Value&)));
            return true;
         }


    }



    return false;
}
bool Dream::AcceptChatKey(ChatKey& sender,ChatKey& rec)
{

    mSender.decode(Base64,sender.signKey,sender.signKey);
    mSender.decode(Base64,sender.encKeyForMe,sender.encKeyForMe);

    mSender.decode(Base64,rec.signKey,rec.signKey);
    mSender.decode(Base64,rec.encKey,rec.encKey);


    mReceiver.decode(RSA,sender.encKeyForMe,sender.encKeyForMe);
    int correct=false;
    mReceiver.verifySign(sender.signKey,sender.encKeyForMe,correct);
   if(!correct)
     return false;

    DataBlock pass,iv,auth;
    pass.CreateFrom(sender.encKeyForMe.Get(),BIT128);
    iv.CreateFrom(sender.encKeyForMe.Get()+BIT128,BIT128);
    auth.CreateFrom(sender.encKeyForMe.Get()+BIT256,BIT128);
    mSender.AcceptAESKeys(pass,iv,auth);
    isInitSender=true;


    mReceiver.decode(RSA,rec.encKey,rec.encKey);
    mSender.verifySign(rec.signKey,rec.encKey,correct);
    if(!correct)
      return false;


    pass.CreateFrom(rec.encKey.Get(),BIT128);
    iv.CreateFrom(rec.encKey.Get()+BIT128,BIT128);
    auth.CreateFrom(rec.encKey.Get()+BIT256,BIT128);
    mReceiver.AcceptAESKeys(pass,iv,auth);
    isInitReceiver=true;


    return true;
}

void Dream::GenChatKey(ChatKey& chat)
{
    DataBlock pass,iv,auth;
    mSender.GenAESKeys(pass,iv,auth);
    pass.Add(iv.Get(),iv.GetLength());
    pass.Add(auth.Get(),auth.GetLength());

    mReceiver.calcSign(pass,chat.signKey);
    mSender.encode(RSA,pass,chat.encKey);
    mReceiver.encode(RSA,pass,chat.encKeyForMe);

    mSender.encode(Base64,chat.signKey,chat.signKey);
    mSender.encode(Base64,chat.encKey,chat.encKey);
    mSender.encode(Base64,chat.encKeyForMe,chat.encKeyForMe);

    isInitSender=true;

}

void Dream::OnRequestArrived(BaseRequest *request,Json::Value& answer )
{

    disconnect(request,SIGNAL(KeyArrived(BaseRequest*,Json::Value&)),this,SLOT(OnRequestArrived(BaseRequest*,Json::Value&)));
    if(dynamic_cast<PublicKeyGetRequest*>(request))
    {

        DataBlock enc;
        enc.CreateFromStr(answer["result"]["encryptionKey"].asString().c_str());
        DataBlock sign;
        sign.CreateFromStr(answer["result"]["signatureKey"].asString().c_str());

        mSender.decode(Base64,enc,enc);
        mSender.decode(Base64,sign,sign);
        mSender.AcceptRSAKeys(enc,sign,false);
        if(mState==ASK_AUTH || mState==CONFIRM_AUTH)
        {
             ChatKey chat;
             GenChatKey(chat);
             if(mState==ASK_AUTH)
                Satory::GetInstance()->AskAuth(mName,chat);
             if(mState==CONFIRM_AUTH)
                 Satory::GetInstance()->ConfirmAuth(request->Get()["params"]["userId"].asString().c_str(),chat);
             mMessageCounter++;
        }
        mState=NONE;
    }
    if(dynamic_cast<ChatKeyGetRequest*>(request))
    {

        ChatKey sender,rec;
        sender.encKeyForMe.CreateFromStr(answer["result"]["sendKeyEncryptedForMe"].asString().c_str());
        sender.signKey.CreateFromStr(answer["result"]["sendKeySignature"].asString().c_str());
        rec.encKey.CreateFromStr((answer["result"]["receiveKeyEncrypted"].asString().c_str()));
        rec.signKey.CreateFromStr((answer["result"]["receiveKeySignature"].asString().c_str()));
        if(AcceptChatKey(sender,rec))
            UpdateMessages();
        mState=NONE;
    }
}
/*
void Dream::SaveKey(DataBlock& key,const std::string& type)
{
    QString filename("key_");
    filename+=type.c_str();


    QString appPath=Master->GetAppPath();

    QDir appKey(appPath+mName),appUser(appPath);
    if(!appKey.exists())
        bool success = appUser.mkdir(mName);

    #ifdef WIN32
    filename=appPath+mName+"\\"+filename+".dat";
     #else
    filename=appPath+mName+"/"+filename+".dat";
    #endif




    QFile f(filename);
    bool val= f.open(QFile::ReadWrite);
    QString err=f.errorString();
    quint64 size=f.write((char*)key.Get(),key.GetLength());
    f.close();
}*/
/*
bool Dream::LoadKey(DataBlock& key,const std::string& type)
{
    QString filename("key_");
    filename+=type.c_str();
    QString appPath=Master->GetAppPath();
    appPath+=mName;
    #ifdef WIN32
         filename=appPath+"\\"+filename+".dat";
     #else
         filename=appPath+"/"+filename+".dat";
    #endif


    QFile f(filename);
    bool val= f.open(QFile::ReadOnly);
    if(val)
     {
        QString err=f.errorString();
        key.Create(f.size());
        f.read((char*)key.Get(),f.size());
        f.close();
        char a;a=0;
        key.Add((unsigned char*)&a,1);
        return true;
    }
    else
        return false;
}*/
void Dream::SendMessage(QString msg)
{
    msgToSend.push_back(msg);

    if(isInitSender && isInitReceiver)
        UpdateMessages();
    else
    {
        if(Satory::GetInstance()->ChatKeyGet(mName))
        {
            ChatKeyGetRequest* req=dynamic_cast<ChatKeyGetRequest*>(Satory::GetInstance()->GetTopRequest());
            if(req)
                connect(req,SIGNAL(KeyArrived(BaseRequest*,Json::Value&)),this,SLOT(OnRequestArrived(BaseRequest*,Json::Value&)));
        }
    }
}

void Dream::AcceptMessage(DataBlock& msg,quint64 counter)
{
    msgToAccept.push_back(pair<quint64,DataBlock>(counter,DataBlock()));
    msgToAccept.front().second.CreateFromBlock(msg);
    if(isInitSender && isInitReceiver)
        UpdateMessages();
    else
    {
        if(Satory::GetInstance()->ChatKeyGet(mName))
        {
            ChatKeyGetRequest* req=dynamic_cast<ChatKeyGetRequest*>(Satory::GetInstance()->GetTopRequest());
            if(req)
                connect(req,SIGNAL(KeyArrived(BaseRequest*,Json::Value&)),this,SLOT(OnRequestArrived(BaseRequest*,Json::Value&)));
        }
    }

}
void Dream::UpdateMessages()
{
    QString my_tag("[");
    my_tag=my_tag+Master->GetName().c_str()+"]: ";

    QString tag("[");
    tag=tag+mName+"]: ";

    list<QString>::iterator it=msgToSend.begin();
    while(it!=msgToSend.end())
    {
        if(mChat)
            mChat->AddMsg(my_tag+(*it));
        DataBlock msg;
        msg.CreateFromStr((*it).toStdString().c_str(),false);
        mSender.SetIVCounter(mMessageCounter);
        mSender.encode(AES,msg,msg);
        mSender.encode(Base64,msg,msg);
        string data((char*)msg.Get());
        Satory::GetInstance()->SendMessage(mName,data.c_str(),mMessageCounter);
        mMessageCounter++;

        it++;
    }

    msgToSend.clear();
    list<pair<quint64,DataBlock> >::iterator ita=msgToAccept.begin();
    while(ita!=msgToAccept.end())
    {
        mReceiver.SetIVCounter(ita->first);
        mReceiver.decode(Base64,ita->second,ita->second);
        mReceiver.decode(AES,ita->second,ita->second);
        unsigned char a=0;ita->second.Add(&a,1);
        string data((char*)ita->second.Get());
        if(mChat)
            mChat->AddMsg(tag+data.c_str());
        ita++;
    }
    msgToAccept.clear();
}
